Scrapboxの開発 - React & Websocketで作るリアルタイムWiki
まじかよymrl.icon
sugoi
つらい
がんばれがんばれgeta6.icon
すげぇ
楽しいkrtx.icon
編集間違えたら ctrl-z or command-z で戻せます
ねむいmakimoto.iconymrl.icon*2makimoto.icon*3asonas.icon わかるshokai.icon*3
がんばれ!!!!!uzulla.icon
え、reacttikit.icon
すごかったminato128.icon
!!ページ右側の「Start presentation」でスライドになります!!
@shokai shokai.icon
株式会社Helpfeel.icon, Inc.
https://gyazo.com/60f40e0ffa7bdb6c23cff0accae8f511
Scrapboxの開発
kininaru
2. 実装のテクニカルなこと
について話します
開発プロセス
shokai.icon 完全リモートワーク、目覚ましをかけずに好きなだけ寝る派
適当に寝て適当に起きる
自分で色々な用途に使ってみる
用途が違うと、ほしい機能も違う
大学の研究室
家族で使う
同人サークル
ゲームの編成・装備・経過のログ取り
色々な機能が欲しくなってくる
でも、なんでも入れるとボタンだらけになってよくない(難しい・・)
techblog 兼 料理レシピ
として使う場合
公開機能が欲しくなる
公開・非公開プロジェクトを設定できるようにしよう
https://gyazo.com/2de46f3af3b62b83c61131ebad14f637
会社や大学の研究室
https://gyazo.com/153d71dccef64cfe0df1937c1aadf4e2
研究ノートを書いてるのに、発表のためにスライドを作り直すのは無駄
スライド作るのが面倒くさい、究極的には発表中に誰かにリアルタイム作成してもらいたい
ページ名が説明的に長くなりがち
長くなってもよい
はてブに上がってくるようなみたいなエモーショナルなタイトルをどんどんつけるべき リンクは、入力補完でなんとかする
https://gyazo.com/18f2b3afad4a624728c566ad66e4fcec
色々な人がいる
OS毎のショートカットキーの再現
https://gyazo.com/13b49b8eb4e8974e454b23dd01b2b9c6
Scrapboxの開発にScrapboxを使う
流れ
1. 好き放題、意見・要望・苦情を書いてもらう
3. Sprint
アレが抜けてない?とか指摘される
4. 実装前に軽く設計をScrapboxに書く
個別のタスク毎に1つずつページを作る
関連ページリストで一覧できる
https://gyazo.com/2e9f3a1ce3048c4a4f8a85259aa75853
やりたい事は100以上ある
個別ページ内で、同時並行して議論している
「やりたい事」の状態では優先度は付けられないし、無理につけない
家の引っ越しとかもこんな感じでやれる
https://gyazo.com/daf5942b896ec78904bd33ec89087cc4
巨大リストを作らない
巨大なToDoリストを見ると気が滅入る
山が高すぎると登る気にならない
ページのネットワークを作る
編集していると、実はこれ同じ機能を要求しているんじゃね?というissue群に気づいたりする
Sprint
https://gyazo.com/edb5f68c4b0f3a5e69f2c96a624c9568
やりたい事は全部scrapbox書いてある
ぶつかり稽古ガチ議論をする
https://gyazo.com/9847a794c9be40654e266aed347a3c0e
リモートワークでも「どれからやっても良いよ」という雰囲気を作れる
たまに緊急で上から降ってくるタスクがあっても、妥当性を感じる
やらされてる感が無くなる
名案が思いついたら突然実装される
https://gyazo.com/ad0ea4ac5bc517db6b375598ca1e08a6
Scrapboxの実装
テクニカルな話
コア
WYSIWYGで画像やリンクも貼れるけど、編集ツールバーやボタンが無いエディタ markdownより簡単なシンタックスで編集・文字装飾できる
Atom.iconを参考にしてReact.iconで実装した
Socket.IO.iconと自作の同期システムでうまいこと同時編集できる
Gitを参考にsubversionみたいなものを実装した
実装環境
エディタの実装
カーソルの縦棒はdiv
緑の範囲選択もdiv3つ
隠し<textarea />
IMEウィンドウを見せるためだけに存在する
カーソルの右側に浮いていて、ついてくる
https://gyazo.com/688d80344a2b353762ec9c79e68a637b
Atom.iconが参考になった
y行目のx文字目の画面上の座標はどこか?
生テキストこんにちはを
1文字ずつ分割して
code:editor.html
<div class='lines'>
<div class='line' id='L1'>
<span class='c-1'>こ</span>
<span class='c-2'>ん</span>
<span class='c-3'>に</span>
<span class='c-4'>ち</span>
<span class='c-5'>は</span>
</div>
</div>
jQueryで位置を取得する
let {left, top} = $('.lines #L1 .c-4').position()
クリックした位置から何行目の何文字目なのか?を求める
逆をやればいい
あとはカーソル移動やemacsキーバインド等を自前で実装すればok
と、よくインターネットに書いてある
jQueryはクロスブラウザで座標の扱いが完璧っぽい!
jQueryでDOM書き換えしなければok
render→componentDidUpdateからjQueryで座標計算→stateに入れる→再render
複数人で同時編集
https://gyazo.com/20b4fe90c13dafd705d1f75d02adb4de
こういう「コミット」をやりとりすればなんとかなるのでは!?
code:commit.json
{
"id": hash,
"parent_id": parent_hash, // 1つ前への参照
}
編集の命令は3種類だけ
1つのcommitのchanegs配列に複数の編集が格納される
insert
隣の行のidを指定して新しい行を追加
code:javascript
{
"_insert": positionId, // 文字を挿入する一つ下の位置にある行のId
"lines": {
"text": text,
"id": lineId // 新しく生成された行のid
}
}
新規に行を挿入する
最後に挿入するときは、特殊IDの_endを指定する。
複数行を挿入するときは、insertを複数個作る
update
idを指定して更新
code:js
{
"_update": lineId, // 変更する行のId
"lines": { "text": text }
}
行を変更する
delete
idを指定して削除
code:js
{
"_delete": lineId, // 削除する行のId
lines: -1
}
行を削除する
コンフリクトしたら?
ユーザー側でmerge画面にするわけにはいかない
コンフリクト判定はサーバー側
clientはとりあえずpushする
pushがrejectされたらpull
git rebaseのような操作をする
気合マージ!!
mergeしよう → コンフリクト → pullしてみる
→ insertが参照していた行が削除されてる、どこにinsertしていいのかわからない!
→ commitをさかのぼって、「削除された行を参照したinsertをした行」を参照してinsertする